/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-05
 * V4.0
 */
package com.jphenix.service.db.datamanager.instancea;

import com.jphenix.kernel.objectloader.interfaceclass.IBeanRegister;
import com.jphenix.kernel.objectloader.interfaceclass.IBeanRegisterChild;
import com.jphenix.kernel.objectpool.instancea.ObjectPool;
import com.jphenix.kernel.objectpool.interfaceclass.IObjectElement;
import com.jphenix.kernel.objectpool.interfaceclass.IObjectPool;
import com.jphenix.service.db.datamanager.interfaceclass.IDataManager;
import com.jphenix.service.db.exception.DBException;
import com.jphenix.share.lang.*;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.DebugUtil;
import com.jphenix.standard.db.DBInfoVO;
import com.jphenix.standard.db.IDataManagerInfo;
import com.jphenix.standard.docs.ClassInfo;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;


/**
 * 数据库连接池管理类
 * com.jphenix.service.db.datamanager.instancea.DataManager
 * 
 * 该类被修改为注册管理类，在插件中如果想加入自己的数据源，调用数据库连接信息类进行注册
 * 到数据库连接池中
 * 
 * 2018-11-12 修改了在没有数据源信息时，获取数据源信息容器为空，抛空指针问题
 * 2018-12-24 将声明的变量类型都改成了接口类型
 * 2019-01-08 mysql数据库比较操蛋，如果设置了utf8mb4编码，在数据库启动后，需要执行语句SET NAMES utf8mb4，如果数据库服务没有配置
 *            就得在初始化连接时执行该语句，于是增加了参数initScript,设置初始化语句，多条语句用;分隔
 * 2019-01-09 修改了后加入数据源信息后，并发时无法获取到数据源信息的错误（在启动时）
 *            增加了移除指定数据源信息（主要用于动态更新数据源连接信息）
 * 2019-01-11 增加了执行初始化脚本日志
 * 2019-03-06 增加了获取指定数据源主键的配置信息包 Map<String,String> getElementInfoMap(String dataSourceName)
 * 2019-04-24 增加了配置文件参数datamanager_no_out_log,可以屏蔽数据库连接池输出日志
 * 2019-11-06 重构了设置连接数据库参数模块
 * 2019-12-26 增加了JNDI模式配置
 * 
 * @author 刘虻
 * 2006-8-13  15:24:53
 */
@ClassInfo({"2019-12-26 11:15","数据库连接池管理类"})
public class DataManager 
			extends ObjectPool
					implements IDataManager,IObjectPool,IBeanRegister,IDataManagerInfo {

    //池元素信息包 key 数据源主键 value HashMap key 参数主键 value参数值
    protected Map<String,Map<String,String>> elementInfoHashMap = null;
    
    //数据源类型容器 key 数据源主键  value数据库类型
    protected Map<Object,String> dataSourceTypeHashMap = null;
    
    protected boolean init = false; //是否初始化
    protected IDataManager parent = null; //父数据库连接池
    protected String basePath = null; //根路径
    
    /**
     * 获取数据库链接线程
     * @author 刘虻
     * 2007-12-24下午09:35:10
     */
    protected class GetConnectionThread extends Thread {
    	
    	protected Connection conn            = null; //数据库链接
    	protected Exception  exception       = null; //异常
    	protected String     jndiName        = null; //JNDI模式中，命名空间名
    	protected String     driveNameStr    = null; //数据库驱动
    	protected String     urlStr          = null; //链接字符串
    	protected String     userStr         = null; //用户名
    	protected String     passWordStr     = null; //密码
    	protected int        loginTimeOutSec = 0;    //登录过程超时时间
    	
    	/**
    	 * 构造函数
    	 * 2007-12-24下午09:40:56
    	 */
    	public GetConnectionThread(
    			 String jndiName
    			,String driveNameStr
    			,String urlStr
    			,String userStr
    			,String passWordStr
    			,int loginTimeOutSec) {
    		super("DataManager-GetConnectionThread");
    		this.jndiName        = jndiName;
    		this.driveNameStr    = driveNameStr;
    		this.urlStr          = urlStr;
    		this.userStr         = userStr;
    		this.passWordStr     = passWordStr;
    		this.loginTimeOutSec = loginTimeOutSec;
    	}
    	
    	/**
    	 * 获取异常信息
    	 * @author 刘虻
    	 * 2007-12-24下午09:46:26
    	 * @return 异常信息
    	 */
    	public Exception getException() {
    		return exception;
    	}
    	
    	/**
    	 * 获取数据库链接
    	 * @author 刘虻
    	 * 2007-12-24下午09:37:18
    	 * @return 数据库链接
    	 */
    	public Connection getConnection() {
    		return conn;
    	}
    	
    	/**
    	 * 覆盖方法
    	 * @author 刘虻
    	 * 2007-12-24下午09:37:53
    	 */
    	@Override
        public void run() {
        	try {
        		if(jndiName!=null && jndiName.length()>0) {
        			//构建JNDI上线文
        			Context ic = new InitialContext();
        			//获取数据源
        			DataSource ds = (DataSource)ic.lookup(jndiName);
        			if(userStr!=null && userStr.length()>0) {
        				conn = ds.getConnection(userStr,passWordStr);
        			}else {
        				conn = ds.getConnection();
        			}
        		}else {
                    //构造新实例
        	        Class.forName(driveNameStr).newInstance(); 
        	        //构造连接对象
        	        DriverManager.setLoginTimeout(loginTimeOutSec);
        	        conn = DriverManager.getConnection(urlStr,userStr,passWordStr);
        		}
        	}catch(Exception e) {
        		e.printStackTrace();
        		try {
        			conn.close();
        		}catch(Exception e2) {}
        		exception = e;
        	}
        
    	}
    }
    
    
    /**
     * 构造函数 
     * @author 刘虻
     * 2006-8-13 15:24:53
     */
    public DataManager() {
        super();
    }
    
    
    /**
     * 覆盖方法 
     * @author  刘虻
     * 2006-8-13 15:25:27
     */
    @Override
    protected IObjectElement initObjectElement(
            	String type, String pk) throws Exception {
        //导入参数合法化
        if (type == null 
                || "".equals(type)) {
           throw new DBException(this,"No Find The Object Type");
        }
        //获得指定的数据源信息
        Map<String,String> dataSourceInfoHasm = getElementInfoMap().get(type);
        if (dataSourceInfoHasm == null) {
            throw new DBException(this,"Not Find DataSource Info By Type:["+type+"]");
        }
        //构造一个新的池元素
        ConnectionImpl element = new ConnectionImpl();
        //设置父类
        element.setPool(this);
        //设置日志类实例
        element.setLog(log);
        //设置类型
        element.setType(type);
        //设置主键
        element.setPK(pk);
        
        //获得对象空闲超时时间（毫秒）
        String value = str(dataSourceInfoHasm.get("defaultNotInUseTime"));
        if(value.length()<1) {
        	value = "600000";
        }
        element.setFreeTimeOut(SLong.valueOf(value));
        
        //设置对象占用超时时间（毫秒）
        value = str(dataSourceInfoHasm.get("defaultTimeOut"));
        if(value.length()<1) {
        	value = "300000";
        }
        element.setUseTimeOut(SLong.valueOf(value));
        
        
        //设置最大加载对象数量
        value = str(dataSourceInfoHasm.get("maxLoadCount"));
        if(value.length()<1) {
        	value = "200";
        }
        element.setMaxObjectCount(SLong.valueOf(value));
        
        //设置验证数据库语句
        element.setValidationQuerySql(str(dataSourceInfoHasm.get("validationQuery")));
        
        //JNDI模式的命名空间名（如果存在该值，其它值可为空，也可以存在账号和密码值）
        String jndiName = prop.getFixParameter(dataSourceInfoHasm.get("jndiName"));
        boolean noJndi = true; //是否不采用JNDI模式
        if(jndiName.length()>0) {
        	noJndi = false;
        	log.dataLog("***The DataSource:["+type+"] Used JNDI Mode***");
        	
        	//从配置文件中直接获取出目标数据库的类型，目前支持以下类型：oracle,h2,mssql,mysql
        	getDataSourceTypeMap().put(type,dataSourceInfoHasm.get("sourceType"));
        }
        //获得数据库驱动信息
        String driveNameStr = prop.getFixParameter(dataSourceInfoHasm.get("driverClassName"));
        if (noJndi && driveNameStr.length()<1) {
            throw new DBException(this,"Not Find The DB Driver Info By Type:["+type+"]");
        }
        
        //数据库连接字符串信息
        String dataBaseUrlStr = prop.getFixParameter(dataSourceInfoHasm.get("databaseUrl"));
        if (noJndi && dataBaseUrlStr.length()<1) {
            throw new DBException(this,"Not Find The DB Link URL Info By Type:["+type+"]");
        }
        
        //替换连接url关键字 替换为文件路径
        String replaceChar = prop.getFixParameter(dataSourceInfoHasm.get("replaceFilePathChar"));
        if (replaceChar.length()>0) {
        	//设置文件路径
        	dataBaseUrlStr = 
        		BaseUtil.swapString(
        				 dataBaseUrlStr
        				,replaceChar
        				,filesUtil.getAllFilePath(
        						prop.getFixParameter(
        								dataSourceInfoHasm.get("dbFilePath"))));
        }
        //数据库登录用户名
        String userNameStr = prop.getFixParameter(dataSourceInfoHasm.get("user"));
        
        //数据库登录密码
        String passWordStr = prop.getFixParameter(dataSourceInfoHasm.get("password"));
        //进行解密
        passWordStr = d64(passWordStr);
        
        //登录超时时间
        int loginTimeOutSec = SInteger.valueOf(dataSourceInfoHasm.get("logintimeout"));
        if(outLog) {
	        log.sqlLog("Init DataBase：\nDataSource："+type+"]\n JNDI:["+jndiName+"] Driver:["+driveNameStr+"]\nURL:["
	                +dataBaseUrlStr+"]\nUserName:["+userNameStr+"] Pwd:["+passWordStr+"]");
        }
        try {
            //构造核心元素
            Connection conn = getNewConnection(
            		jndiName
                    ,driveNameStr
                    ,dataBaseUrlStr
                    ,userNameStr
                    ,passWordStr
                    ,loginTimeOutSec);
            if(outLog) {
            	log.sqlLog("Init DataBase Complete");
            }
            if (conn==null) {
                throw new DBException(this,"Get DataBase Connection Error");
            }
            if(pk==null || pk.length()<1) {
                //设置是否自动提交
                String autoCommit = 
                		prop.getFixParameter(dataSourceInfoHasm.get("autocommit"));
                if(autoCommit.length()>0) {
                    conn.setAutoCommit(SBoolean.valueOf(autoCommit));
                }
            }else {
                //需要做事务处理
                conn.setAutoCommit(false);
            }
            
            //执行初始化
            String initScript = prop.getFixParameter(dataSourceInfoHasm.get("initScript"));
            if(initScript.length()>0) {
            	//执行初始化脚本
            	initScript(conn,initScript);
            }
            
            //放入核心类
            element.setKernel(conn);
        }catch(Exception e) {
        	//构造错误提示信息
        	StringBuffer sbf = new StringBuffer();
        	sbf
        		.append("Get New Connection Exception Drive:[")
        		.append(driveNameStr)
        		.append("] DBUrl:[")
        		.append(dataBaseUrlStr)
        		.append("] UserName:[")
        		.append(userNameStr)
        		.append("] DBUrl:[")
        		.append(dataBaseUrlStr)
        		.append("] UserName:[")
        		.append(userNameStr)
        		.append("] PWD:[")
        		.append(passWordStr)
        		.append("]");
            throw new DBException(this,sbf.toString());
        }
        return element;
    }
    
    
    /**
     * 执行初始化脚本
     * @param conn 数据库连接对象
     * @param sql  建立连接时（初始化时）需要执行的语句
     * 2019年1月8日
     * @author MBG
     */
    private void initScript(Connection conn,String sql) {
    	//分隔多个sql语句
    	String[] sqls = sql.split(";");
    	if(sqls==null || sqls.length<1) {
    		return;
    	}
    	if(outLog) {
    		log.sqlLog("----------Begin Execute Init Script ["+sql+"]");
    	}
    	PreparedStatement stmt = null; 
    	for(int i=0;i<sqls.length;i++) {
    		sqls[i] = sqls[i].trim();
    		if(sqls[i].length()<1) {
    			continue;
    		}
    		try {
    			stmt = conn.prepareStatement(sqls[i]);
    			stmt.executeUpdate();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			try {
    			    stmt.close();
    			}catch(Exception e1) {}
    			stmt = null;
    		}
    	}
    }
    
    /**
     * 构造新的数据库连接对象
     * @author 刘虻
     * @param jndiName     JNDI模式的命名空间名（为空时采用普通连接模式，如果存在该值，其它值可为空）
     * @param driveNameStr 数据库驱动路径
     * @param urlStr       连接字符串
     * @param userStr      登录用户名
     * @param passWordStr  登录密码
     * @return             数据库连接对象实例
     * @throws Exception   构造数据库连接对象时发生异常
     * 2006-4-3 17:24:52
     * @deprecated
     */
    protected Connection getNewConnection(
    		 String jndiName
            ,String driveNameStr
            ,String urlStr
            ,String userStr
            ,String passWordStr
            ,int loginTimeOutSec) throws Exception {
    	//构建获取数据库链接线程
    	GetConnectionThread gt =
    		new GetConnectionThread(jndiName,driveNameStr,urlStr,userStr,passWordStr,loginTimeOutSec);
    	gt.start(); //启动线程
    	try {
	    	//等待执行
	    	gt.join(loginTimeOutSec*1000);
    	}catch(Exception e) {}
    	if (gt.isAlive()) {
    		//打印堆栈
    		BaseUtil.printStackTraceString(gt);
    		gt.stop();
    		throw new DBException(
    				this,"Get DataBaseConnection Time Out DriveName:["
        			+driveNameStr+"] Url:["+urlStr+"] User:["+userStr
        			+"] OutTime:["+loginTimeOutSec+"]");
    	}
    	if (gt.getException()!=null) {
    		gt.getException().printStackTrace();
    		throw gt.getException();
    	}
    	return gt.getConnection();
    }

    /**
     * 覆盖方法 
     * @author  刘虻
     * 2006-8-13 15:25:27
     */
    @Override
    protected boolean checkElementError(IObjectElement element) {
        if (element==null) {
            //对象为空
            return true;
        }
        if (!(element instanceof ConnectionImpl)) {
            //不是当前池元素类
            return true;
        }
        try {
	        if ((((ConnectionImpl)element)).getKernel()==null) {
	            //核心类为空
	            return true;
	        }
        }catch(Exception e) {
        	return true;
        }
        return false;
    }

    /**
     * 覆盖方法 
     * @author  刘虻
     * 2006-8-13 15:25:27
     */
    @Override
    public Connection getConnection(
            String dataSourceName) throws Exception {
        return getConnection(dataSourceName,null);
    }

    /**
     * 覆盖方法 
     * @author  刘虻
     * 2006-8-13 15:25:27
     */
    @Override
    public Connection getConnection(
    		String dataSourceName, long timeOut) throws Exception {
        //获取池元素
        ConnectionImpl conn = 
            (ConnectionImpl)getConnection(dataSourceName,null); //返回值
        //设置超时间隔时间
        conn.setUseTimeOut(timeOut);
        return conn;
    }

    /**
     * 覆盖方法 
     * @author  刘虻
     * 2006-8-13 15:25:27
     */
    @Override
    public Connection getConnection(
            String dataSourceName, String pk) throws Exception {
    	if(dataSourceName==null || dataSourceName.length()<1) {
    		throw new DBException(this,"DataSourceName Is Null");
    	}
    	if(!getDataSourceTypeMap().containsKey(dataSourceName)) {
    		if(parent==null) {
    			throw new DBException(this,"Not Find The DataSourceName:["+dataSourceName
    					+"] SourcNames:["+DebugUtil.getMapKeyString(getDataSourceTypeMap())+"] \n===DataManager:["+this.hashCode()+"]");
    		}
    		return parent.getConnection(dataSourceName,pk);
    	}else {
    		if(outLog) {
    			log.sqlLog("===DataManager:["+this.hashCode()+"]");
    		}
    	}
        ConnectionImpl conn = null; //返回值
        try {
	        //登记元素
	        conn = (ConnectionImpl)getObjectInstance(dataSourceName,pk);
	        if(conn.isClosed()) {
	        	//这个连接已经挂了，只能关闭
	        	closeElement(dataSourceName,null); //关闭元素
	        	throw new DBException(this,"  DataSource:["+dataSourceName+"] Session PK:["+pk+"] Has Closed");
	        }
	        if(outLog) {
	        	log.sqlLog("  DataSource:["+dataSourceName+"] Session PK:["+pk+"]");
	        }
	        return conn;
        }catch(Exception e) {
            closeElement(dataSourceName,null); //关闭元素
            throw new DBException(this,"Get New Object Exception:"+DebugUtil.outException(e));
        }
    }
    
    
    /**
     * 获取数据库连接
     * @author 刘虻
     * 2009-9-21下午08:41:23
     * @param dataSourceName 数据源主键
     * @param pk 会话主键
     * @param autoCommit 是否自动提交
     * @return 数据库连接
     * @throws DBException 执行发生异常
     */
    @Override
    public Connection getConnection(
    		String dataSourceName
    		,String pk
    		,boolean autoCommit) throws Exception {
    	if(dataSourceName==null || dataSourceName.length()<1) {
    		throw new DBException(this,"DataSourceName Is Null");
    	}
    	if(!getDataSourceTypeMap().containsKey(dataSourceName)) {
    		if(parent==null) {
    			throw new DBException(this,"Not Find The DataSourceName:["+dataSourceName+"]");
    		}
    		return parent.getConnection(dataSourceName,pk,autoCommit);
    	}
        ConnectionImpl conn = null; //返回值
        try {
	        //登记元素
	        conn = (ConnectionImpl)getObjectInstance(dataSourceName,pk);
	        if(conn.isClosed()) {
	        	//这个连接已经挂了，只能关闭
	        	closeElement(dataSourceName,null); //关闭元素
	        	throw new DBException(this,"  DataSource:["+dataSourceName+"] Session PK:["+pk+"] Has Closed");
	        }
	        if(outLog) {
	        	log.sqlLog("  DataSource:["+dataSourceName+"] Session PK:["+pk+"]");
	        }
	        //设置是否自动提交
	        conn.doSetAutoCommit(autoCommit);
	        return conn;
        }catch(Exception e) {
            closeElement(dataSourceName,null); //关闭元素
            throw new DBException(this,"Get New Object Exception:"+DebugUtil.outException(e));
        }
    }

    /**
     * 覆盖方法 
     * @author  刘虻
     * 2006-8-13 15:25:27
     */
    @Override
    public Connection getConnection(
            String dataSourceName, String pk, long timeOut) throws Exception {
        //获取池元素
        ConnectionImpl conn = 
            (ConnectionImpl)getConnection(dataSourceName,pk); //返回值
        //设置超时间隔时间
        conn.setUseTimeOut(timeOut);
        return conn;
    }

    /**
     * 覆盖方法 
     * @author  刘虻
     * 2006-8-13 15:25:27
     */
    @Override
    public String getDataSourceType(String dataSourceNameStr) {
        //导入参数合法化
        if (dataSourceNameStr == null 
                || !getElementInfoMap().containsKey(dataSourceNameStr)) {
            log.error("Not Find The DataSource Info:"+dataSourceNameStr,null);
            return null;
        }
        return getDataSourceTypeMap().get(dataSourceNameStr);
    }

    
    
    /**
     * 获得与数据源名称对应的数据源类型
     * @author 刘虻
     * @return 与数据源名称对应的数据源类型
     * 2006-4-3  17:57:46
     */
    protected synchronized Map<Object,String> getDataSourceTypeMap() {
        if (dataSourceTypeHashMap == null) {
            //构造新实例
            dataSourceTypeHashMap = new HashMap<Object,String>();
            //获得所有数据源的名称
            Object[] keys = 
                BaseUtil.getMapKeys(getElementInfoMap());
            if (keys != null && keys.length > 0) {
                for (int i=0;i<keys.length;i++) {
                    //获得一个数据源信息
                    Map<String,String> hasm = getElementInfoMap().get(keys[i]);
                    if (hasm == null) {
                        continue;
                    }
                    //获得连接字符串
                    String urlStr = str(hasm.get("url"));
                    if(urlStr.length()<1) {
                    	urlStr = str(hasm.get("db_url"));
                    }
                    if (urlStr.length()<1) {
                        continue;
                    }
                    //整理参数
                    urlStr = prop.getFixParameter(urlStr);
                    //设置数据库类型
                    dataSourceTypeHashMap.put(keys[i],getSourceType(urlStr));
                }
            }
        }
        return dataSourceTypeHashMap;
    }
    
    /**
     * 覆盖方法 
     * 原本还需要传入数据源主键，
     * 但通常会话只存在于其中一个数据源中，没必要再传入数据源主键
     * @author  刘虻
     * 2006-8-13 15:25:27
     */
    @Override
    public void commit(String sessionKey) throws DBException {
        //获取该类型所有的元素
        List<IObjectElement> elements = getAllTypeElementByPk(sessionKey);
        for(IObjectElement conn:elements) {
            if (conn==null) {
                continue;
            }
            if (sessionKey!=null && sessionKey.equals(conn.getPK())) {
            	try {
            		((ConnectionImpl)conn).doCommit();
            	}catch(Exception e) {}
                break;
            }else if (sessionKey==null) {
            	try {
            		((ConnectionImpl)conn).doCommit();
            	}catch(Exception e) {}
            }
        }
    }

    /**
     * 覆盖方法 
     * 原本还需要传入数据源主键，
     * 但通常会话只存在于其中一个数据源中，没必要再传入数据源主键
     * @author  刘虻
     * 2006-8-13 15:25:27
     */
    @Override
    public void rollback(String sessionKey) throws DBException {
        //获取该类型所有的元素
    	/*
    	 * 一个主键可以对应多个类型
    	 */
        List<IObjectElement> elements = getAllTypeElementByPk(sessionKey);
        for(IObjectElement conn:elements) {
            if (conn==null) {
                continue;
            }
            synchronized (conn) {
        		try {
        			((ConnectionImpl)conn).doRollback();
        		}catch(Exception e) {}
        		//如果数据库有问题，则关闭连接
        		if(!((ConnectionImpl)conn).checkConnection()) {
        			closeElement(conn);
        		}
			}
        }
    }

    /**
     * 覆盖方法  由数据库连接池托管
     * 原本还需要传入数据源主键，
     * 但通常会话只存在于其中一个数据源中，没必要再传入数据源主键
     * @author  刘虻
     * 2006-8-13 15:25:27
     */
    @Override
    public void close(String sessionKey) throws DBException {
        //获取该类型所有的元素
        /*
         * 一个主键可以对应多个类型
         */
        List<IObjectElement> elements = getAllTypeElementByPk(sessionKey);
        for(IObjectElement conn:elements) {
            if (conn==null) {
                continue;
            }
            synchronized (conn) {
                try {
                    ((ConnectionImpl)conn).doClose();
                }catch(Exception e) {}
                //如果数据库有问题，则关闭连接
                if(!((ConnectionImpl)conn).checkConnection()) {
                    closeElement(conn);
                }
            }
        }
    }
    
    
    /**
     * 执行初始化
     * @author 刘虻
     * 2006-7-26  14:03:14
     */
    @Override
    public void init() {
    	if(init) {
    		return;
    	}
    	//禁止输出日志
    	if(boo(p("datamanager_no_out_log"))) {
    		outLog = false;
    	}
        //标记已经初始化
        init = true;
        super.startPool(); //启动池
    }
    

	/**
	 * 获取配置信息包
	 * @author 刘虻
	 * 2007-3-15上午10:20:01
	 * @return 配置信息包
	 */
	@Override
    public synchronized Map<String,Map<String,String>> getElementInfoMap() {
		if(elementInfoHashMap==null) {
			elementInfoHashMap = new HashMap<String,Map<String,String>>();
		}
		return elementInfoHashMap;
	}
	
	
	/**
	 * 获取指定数据源的配置信息包
	 * @param dataSourceName 数据源主键
	 * @return 指定数据源的配置信息包
	 * 2019年3月6日
	 * @author MBG
	 */
	@Override
    public Map<String,String> getElementInfoMap(String dataSourceName){
		Map<String,String> res = getElementInfoMap().get(dataSourceName);
		if(res==null) {
			return new HashMap<String,String>();
		}
		return res;
	}
	
	
	/**
	 * 获取数据源主键序列
	 * 刘虻
	 * 2010-10-27 上午09:58:00
	 * @return 数据源主键序列
	 */
	@Override
    public List<String> getDataSourceKeyList() {
		return BaseUtil.getMapKeyList(getElementInfoMap());
	}


	/**
	 * 设置配置信息包
	 * @author 刘虻
	 * 2007-3-15上午10:20:11
	 * @param elementInfoHashMap 配置信息包
	 */
	public void setElementInfoMap(Map<String,Map<String,String>> elementInfoHashMap) {
		this.elementInfoHashMap = elementInfoHashMap;
	}

	
	/**
	 * 增加数据源信息元素
	 * 刘虻
	 * 2010-5-4 下午02:45:40
	 * @param elementInfoMap 数据源信息元素容器
	 * @throws Exception 执行发生异常
	 */
	public synchronized void addElementInfoMap(Map<String,String> elementInfoMap) throws Exception {
		if(elementInfoMap==null) {
			return;
		}
		//数据库连接池主键
		String name = SString.valueOf(elementInfoMap.get("name"));
		if(name.length()<1) {
			throw new DBException(this,"Not Find The DataSourceName key:[name] Info");
		}
		//JNDI模式的命名空间名
		String jndiName = str(elementInfoMap.get("jndiName"));
        //获得连接字符串
        String urlStr = str(elementInfoMap.get("databaseUrl"));
        if (jndiName.length()<1 && urlStr.length()<1) {
        	urlStr = str(elementInfoMap.get("db_url"));
        	if(urlStr.length()<1) {
        		throw new DBException(this,"Not Find The DataSourceURL key:[url] Info");
        	}
        }
		getElementInfoMap().put(name,elementInfoMap);
		
		if(dataSourceTypeHashMap==null) {
			getDataSourceTypeMap(); //构造数据源类型容器
		}
		if(urlStr.length()>0) {
			//整理参数
	        urlStr = prop.getFixParameter(urlStr);
	        //设置数据源类型
	        dataSourceTypeHashMap.put(name,getSourceType(urlStr));
		}else {
			dataSourceTypeHashMap.put(name,str(elementInfoMap.get("sourceType")));
		}
	}
	
	/**
	 * 移除数据源配置信息
	 * @param source  数据源主键
	 * 2019年1月9日
	 * @author MBG
	 */
	public synchronized void removeSource(String source) {
		getElementInfoMap().remove(source);
		getDataSourceTypeMap().remove(source);
		//从池中移除该数据源
		SListMap<IObjectElement> pool = getObjectPoolMap().remove(source);
		if(pool!=null) {
			IObjectElement element; //连接对象类实例
			for(int i=0;i<pool.size();i++) {
				element = pool.remove(i);
				try {
					element.terminat();
				}catch(Exception e) {}
			}
		}
	}
	
	/**
	 * 通过连接字符串返回数据库类型
	 * @param url    连接字符串
	 * @return       数据库类型
	 * 2019年1月9日
	 * @author MBG
	 */
	private String getSourceType(String url) {
		if(url==null) {
			return DATA_BASE_TYPE_NORMAL;
		}
        if (url.indexOf("mysql") > -1) {
        	return DATA_BASE_TYPE_MYSQL;
        }else if (url.indexOf("oracle") > -1) {
            return DATA_BASE_TYPE_ORACLE;
        }else if (url.indexOf("microsoft") > -1 || url.indexOf("sqlserver") > -1) {
            return DATA_BASE_TYPE_MSSQL;
        }else if(url.indexOf("h2") > -1) {
            return DATA_BASE_TYPE_H2;
        }
    	//按照普通标准数据库操作
    	return DATA_BASE_TYPE_NORMAL;
	}
	
	

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-3-15上午10:23:07
	 * @deprecated
	 */
    @Override
    public void destory() {
		super.terminat();
	}

	/**
	 * 设置父数据库连接池
	 * 刘虻
	 * 2010-5-4 下午02:32:39
	 * @param parent 父数据库连接池
	 */
	public void setDataManager(IDataManager parent) {
		this.parent = parent;
	}


	/**
	 * 获取类名
	 * @author 刘虻
	 * 2007-7-5下午12:24:31
	 * @return 类名
	 */
	@Override
    public String getClassName() {
		return "DataManager";
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-6-22 上午11:19:45
	 */
	@Override
    public void unRegist(Object bean) {
		if(bean==null || !(bean instanceof DBInfoVO)) {
			return;
		}
		//信息容器
		Map<String,String> infoMap = ((DBInfoVO)bean).getDBInfoMap();
		if(infoMap==null) {
			return;
		}
		//数据库连接池主键
		String name = SString.valueOf(infoMap.get("name"));
		if(name.length()<1) {
			return;
		}
        if(bean instanceof IBeanRegisterChild) {
            ((IBeanRegisterChild)bean).beforeUnRegister();
        }
		getElementInfoMap().remove(name);
		return;
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-6-22 上午11:19:45
	 */
	@Override
    public boolean regist(Object bean) {
		if(bean==null || !(bean instanceof DBInfoVO)) {
			return false;
		}
		try {
			addElementInfoMap(((DBInfoVO)bean).getDBInfoMap());
			
            if(bean instanceof IBeanRegisterChild) {
                ((IBeanRegisterChild)bean).setRegister(this);
                ((IBeanRegisterChild)bean).afterRegister();
            }
			
			return true;
		}catch(Exception e) {
			e.printStackTrace();
		}
		return false;
	}
}
